#!/usr/bin/env node
import * as fs from 'fs';
import * as path from 'path';
import { MDZFile } from './mdzFile';
import { MDZErrorType, MDZError } from './types';
import { calculateChecksum } from './utils';

const args = process.argv.slice(2);
const command = args[0];

// 版本号
const version = '1.0.0';

// 显示版本信息
function showVersion() {
  console.log(`MDZ CLI v${version}`);
}

// 显示帮助信息
async function showHelp() {
  console.log(`
MDZ CLI - Command line tool for MarkdownZip files v${version}

Usage:
  mdz create <output.mdz> <input.md> [--password=<password>] [--title=<title>] [--author=<author>]
  mdz extract <input.mdz> <output.md> [--password=<password>] [--content-only] [--assets-only] [--overwrite]
  mdz info <input.mdz> [--password=<password>]
  mdz add <mdz_file> <asset_file> [--password=<password>] [--relative-path=<path>] [--skip-duplicate]
  mdz list <mdz_file> [--password=<password>] [--json]
  mdz metadata <mdz_file> [--password=<password>] [--title=<title>] [--author=<author>] [--json=<json>]
  mdz password <mdz_file> [--password=<password>] [--new-password=<password>] [--remove]
  mdz verify <mdz_file> [--password=<password>]
  mdz extract-asset <mdz_file> <asset_id> <output_path> [--password=<password>] [--overwrite]
  mdz version
  mdz help
  
Global Options:
  --password=<password>     Password for encrypted MDZ files
  --verbose                 Show detailed information
  --quiet                   Reduce output information
  --force                   Force operation, ignoring warnings

Examples:
  mdz create example.mdz README.md --title="Example Document" --author="Author"
  mdz extract example.mdz extracted.md --password=123456
  mdz info example.mdz
  mdz add example.mdz image.png
  mdz list example.mdz --json
  mdz metadata example.mdz --title="New Title" --author="New Author"
  mdz password example.mdz --new-password=newpass
  mdz verify example.mdz
  mdz extract-asset example.mdz 5f2d8da6-e716-4c0e-8b0c-ec92d0b44dbd ./output.png
`);
}

// 解析命令行参数
function parseArgs(args: string[]) {
  const result: any = { _: [] };
  
  for (const arg of args) {
    if (arg.startsWith('--')) {
      const parts = arg.substring(2).split('=');
      const key = parts[0];
      const value = parts.length > 1 ? parts.slice(1).join('=') : true;
      result[key] = value;
    } else if (arg.startsWith('-')) {
      // 支持短选项
      const shortOption = arg.substring(1);
      switch (shortOption) {
        case 'p':
          // 处理密码短选项，但需要下一个参数是密码值
          if (args.indexOf(arg) < args.length - 1 && !args[args.indexOf(arg) + 1].startsWith('-')) {
            result.password = args[args.indexOf(arg) + 1];
          } else {
            result.p = true; // 如果没有值，则设为true
          }
          break;
        case 'v':
          result.verbose = true;
          break;
        case 'q':
          result.quiet = true;
          break;
        case 'f':
          result.force = true;
          break;
        case 't':
          // 处理标题短选项
          if (args.indexOf(arg) < args.length - 1 && !args[args.indexOf(arg) + 1].startsWith('-')) {
            result.title = args[args.indexOf(arg) + 1];
          }
          break;
        case 'a':
          // 处理作者短选项
          if (args.indexOf(arg) < args.length - 1 && !args[args.indexOf(arg) + 1].startsWith('-')) {
            result.author = args[args.indexOf(arg) + 1];
          }
          break;
        case 'o':
          // 处理输出路径短选项
          if (args.indexOf(arg) < args.length - 1 && !args[args.indexOf(arg) + 1].startsWith('-')) {
            result.output = args[args.indexOf(arg) + 1];
          }
          break;
        case 'r':
          // 处理相对路径短选项
          if (args.indexOf(arg) < args.length - 1 && !args[args.indexOf(arg) + 1].startsWith('-')) {
            result['relative-path'] = args[args.indexOf(arg) + 1];
          }
          break;
        default:
          result[shortOption] = true;
      }
    } else {
      result._.push(arg);
    }
  }
  
  return result;
}

// 输出日志的工具函数
function log(message: string, options: any = {}, level: 'info' | 'error' | 'warn' | 'verbose' = 'info') {
  // 如果是安静模式，只输出错误
  if (options.quiet && level !== 'error') {
    return;
  }
  
  // 如果是详细模式，输出所有
  if (level === 'verbose' && !options.verbose) {
    return;
  }
  
  switch (level) {
    case 'error':
      console.error(message);
      break;
    case 'warn':
      console.warn(message);
      break;
    case 'verbose':
      console.log(`[VERBOSE] ${message}`);
      break;
    case 'info':
    default:
      console.log(message);
  }
}

// 创建MDZ文件
async function createMDZ(parsedArgs: any) {
  const outputPath = parsedArgs._[0];
  const inputPath = parsedArgs._[1];
  
  if (!outputPath || !inputPath) {
    log('Error: Output and input file paths are required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(inputPath)) {
    log(`Error: Input file "${inputPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  // 检查输出文件是否存在，如果存在且没有force选项则提示
  if (fs.existsSync(outputPath) && !parsedArgs.force) {
    log(`Error: Output file "${outputPath}" already exists. Use --force to overwrite.`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 读取输入文件，使用buffer处理，避免编码问题
    const content = fs.readFileSync(inputPath, 'utf8');
    
    // 创建MDZ文件
    const mdz = new MDZFile(outputPath);
    await mdz.create(content, {
      title: parsedArgs.title || path.basename(inputPath, path.extname(inputPath)),
      author: parsedArgs.author || '',
      password: parsedArgs.password
    });
    
    // 保存文件
    await mdz.save();
    await mdz.close();
    
    log(`MDZ file created successfully: ${outputPath}`, parsedArgs);
  } catch (err: any) {
    handleError(err, 'Failed to create MDZ file', parsedArgs);
  }
}

// 提取MDZ文件
async function extractMDZ(parsedArgs: any) {
  const inputPath = parsedArgs._[0];
  const outputPath = parsedArgs._[1];
  
  if (!inputPath || !outputPath) {
    log('Error: Input MDZ file and output file paths are required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(inputPath)) {
    log(`Error: MDZ file "${inputPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  // 检查输出文件是否存在，如果存在且没有overwrite选项则提示
  if (fs.existsSync(outputPath) && !parsedArgs.overwrite && !parsedArgs.force) {
    log(`Error: Output file "${outputPath}" already exists. Use --overwrite or --force to overwrite.`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(inputPath);
    await mdz.open(parsedArgs.password);
    
    // 获取内容
    const content = await mdz.getMainContent();
    
    // 写入输出文件，使用BOM标记UTF-8编码
    const bomPrefix = Buffer.from([0xEF, 0xBB, 0xBF]); // UTF-8 BOM
    const contentBuffer = Buffer.from(content, 'utf8');
    const fileBuffer = Buffer.concat([bomPrefix, contentBuffer]);
    fs.writeFileSync(outputPath, fileBuffer);
    
    // 关闭MDZ文件
    await mdz.close();
    
    log(`Content extracted successfully to: ${outputPath}`, parsedArgs);
  } catch (err: any) {
    handleError(err, 'Failed to extract MDZ file', parsedArgs);
  }
}

// 提取资源文件
async function extractAsset(parsedArgs: any) {
  const mdzPath = parsedArgs._[0];
  const assetId = parsedArgs._[1];
  const outputPath = parsedArgs._[2];
  
  if (!mdzPath || !assetId || !outputPath) {
    log('Error: MDZ file, asset ID, and output path are required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(mdzPath)) {
    log(`Error: MDZ file "${mdzPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  // 检查输出文件是否存在，如果存在且没有overwrite选项则提示
  if (fs.existsSync(outputPath) && !parsedArgs.overwrite && !parsedArgs.force) {
    log(`Error: Output file "${outputPath}" already exists. Use --overwrite or --force to overwrite.`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(mdzPath);
    await mdz.open(parsedArgs.password);
    
    // 提取资源
    await mdz.extractAsset(assetId, outputPath);
    
    // 关闭MDZ文件
    await mdz.close();
    
    log(`Asset extracted successfully to: ${outputPath}`, parsedArgs);
  } catch (err: any) {
    handleError(err, 'Failed to extract asset', parsedArgs);
  }
}

// 显示MDZ文件信息
async function showInfo(parsedArgs: any) {
  const inputPath = parsedArgs._[0];
  
  if (!inputPath) {
    log('Error: MDZ file path is required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(inputPath)) {
    log(`Error: MDZ file "${inputPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(inputPath);
    await mdz.open(parsedArgs.password);
    
    // 获取元数据
    const metadata = mdz.getMetadata();
    
    // 获取资源列表
    const assets = mdz.getAssets();
    
    // 显示信息
    log('MDZ File Information:', parsedArgs);
    log('--------------------', parsedArgs);
    log(`File: ${inputPath}`, parsedArgs);
    log(`Title: ${metadata.title || 'Untitled'}`, parsedArgs);
    log(`Author: ${metadata.author || 'Unknown'}`, parsedArgs);
    log(`Version: ${metadata.version}`, parsedArgs);
    log(`Created: ${new Date(metadata.createdAt).toLocaleString()}`, parsedArgs);
    log(`Updated: ${new Date(metadata.updatedAt).toLocaleString()}`, parsedArgs);
    log(`Encrypted: ${metadata.encrypted ? 'Yes' : 'No'}`, parsedArgs);
    log(`Asset Count: ${assets.length}`, parsedArgs);
    
    if (assets.length > 0) {
      log('\nAssets:', parsedArgs);
      assets.forEach((asset, index) => {
        log(`${index + 1}. ID: ${asset.id}`, parsedArgs);
        log(`   Name: ${asset.originalName}`, parsedArgs);
        log(`   Type: ${asset.mimeType}`, parsedArgs);
        log(`   Size: ${asset.size} bytes`, parsedArgs);
        log(`   Path: ${asset.path}`, parsedArgs);
        log('', parsedArgs);
      });
    }
    
    // 关闭MDZ文件
    await mdz.close();
  } catch (err: any) {
    handleError(err, 'Failed to show MDZ file information', parsedArgs);
  }
}

// 添加资源到MDZ文件
async function addAsset(parsedArgs: any) {
  const mdzPath = parsedArgs._[0];
  const assetPath = parsedArgs._[1];
  
  if (!mdzPath || !assetPath) {
    log('Error: MDZ file and asset file paths are required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(mdzPath)) {
    log(`Error: MDZ file "${mdzPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(assetPath)) {
    log(`Error: Asset file "${assetPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(mdzPath);
    await mdz.open(parsedArgs.password);
    
    // 添加资源
    const relativePath = parsedArgs['relative-path'] || '';
    const assetId = await mdz.addAsset(assetPath, relativePath);
    
    // 保存文件
    await mdz.save();
    
    // 关闭MDZ文件
    await mdz.close();
    
    log(`Asset added successfully: ${path.basename(assetPath)}`, parsedArgs);
    log(`Asset ID: ${assetId}`, parsedArgs);
  } catch (err: any) {
    handleError(err, 'Failed to add asset', parsedArgs);
  }
}

// 列出MDZ文件中的所有资源
async function listAssets(parsedArgs: any) {
  const mdzPath = parsedArgs._[0];
  
  if (!mdzPath) {
    log('Error: MDZ file path is required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(mdzPath)) {
    log(`Error: MDZ file "${mdzPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(mdzPath);
    await mdz.open(parsedArgs.password);
    
    // 获取资源列表
    const assets = mdz.getAssets();
    
    if (parsedArgs.json) {
      // 以JSON格式输出
      console.log(JSON.stringify(assets, null, 2));
    } else {
      // 显示资源列表
      log(`Assets in ${mdzPath}:`, parsedArgs);
      log('--------------------', parsedArgs);
      
      if (assets.length === 0) {
        log('No assets found in this MDZ file.', parsedArgs);
      } else {
        assets.forEach((asset, index) => {
          log(`${index + 1}. ID: ${asset.id}`, parsedArgs);
          log(`   Name: ${asset.originalName}`, parsedArgs);
          log(`   Type: ${asset.mimeType}`, parsedArgs);
          log(`   Size: ${asset.size} bytes`, parsedArgs);
          log(`   Path: ${asset.path}`, parsedArgs);
          log('', parsedArgs);
        });
      }
    }
    
    // 关闭MDZ文件
    await mdz.close();
  } catch (err: any) {
    handleError(err, 'Failed to list assets', parsedArgs);
  }
}

// 修改MDZ文件的元数据
async function updateMetadata(parsedArgs: any) {
  const mdzPath = parsedArgs._[0];
  
  if (!mdzPath) {
    log('Error: MDZ file path is required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(mdzPath)) {
    log(`Error: MDZ file "${mdzPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(mdzPath);
    await mdz.open(parsedArgs.password);
    
    // 准备元数据更新
    const metadataUpdate: any = {};
    
    if (parsedArgs.title) {
      metadataUpdate.title = parsedArgs.title;
    }
    
    if (parsedArgs.author) {
      metadataUpdate.author = parsedArgs.author;
    }
    
    // 如果提供了JSON数据，解析并合并
    if (parsedArgs.json) {
      try {
        const jsonData = JSON.parse(parsedArgs.json);
        Object.assign(metadataUpdate, jsonData);
      } catch (e) {
        log(`Error: Invalid JSON data: ${e}`, parsedArgs, 'error');
        await mdz.close();
        return;
      }
    }
    
    // 如果没有任何更新项，提示
    if (Object.keys(metadataUpdate).length === 0) {
      log('Error: No metadata updates provided. Use --title, --author, or --json options.', parsedArgs, 'error');
      await mdz.close();
      return;
    }
    
    // 更新元数据
    mdz.updateMetadata(metadataUpdate);
    
    // 保存文件
    await mdz.save();
    
    // 关闭MDZ文件
    await mdz.close();
    
    log(`Metadata updated successfully for: ${mdzPath}`, parsedArgs);
  } catch (err: any) {
    handleError(err, 'Failed to update metadata', parsedArgs);
  }
}

// 设置或修改MDZ文件的密码
async function changePassword(parsedArgs: any) {
  const mdzPath = parsedArgs._[0];
  
  if (!mdzPath) {
    log('Error: MDZ file path is required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(mdzPath)) {
    log(`Error: MDZ file "${mdzPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  // 检查是否提供了新密码或移除选项
  if (!parsedArgs['new-password'] && !parsedArgs.remove) {
    log('Error: Either --new-password or --remove option is required', parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(mdzPath);
    await mdz.open(parsedArgs.password);
    
    // 设置新密码或移除密码
    if (parsedArgs.remove) {
      await mdz.setPassword(null, parsedArgs.password);
      log(`Password removed successfully for: ${mdzPath}`, parsedArgs);
    } else {
      await mdz.setPassword(parsedArgs['new-password'], parsedArgs.password);
      log(`Password changed successfully for: ${mdzPath}`, parsedArgs);
    }
    
    // 保存文件
    await mdz.save();
    
    // 关闭MDZ文件
    await mdz.close();
  } catch (err: any) {
    handleError(err, 'Failed to change password', parsedArgs);
  }
}

// 验证MDZ文件的完整性
async function verifyMDZ(parsedArgs: any) {
  const mdzPath = parsedArgs._[0];
  
  if (!mdzPath) {
    log('Error: MDZ file path is required', parsedArgs, 'error');
    return;
  }
  
  if (!fs.existsSync(mdzPath)) {
    log(`Error: MDZ file "${mdzPath}" does not exist`, parsedArgs, 'error');
    return;
  }
  
  try {
    // 打开MDZ文件
    const mdz = new MDZFile(mdzPath);
    await mdz.open(parsedArgs.password);
    
    // 获取元数据
    const metadata = mdz.getMetadata();
    
    // 获取资源列表
    const assets = mdz.getAssets();
    
    // 验证每个资源的校验和
    let allValid = true;
    let validCount = 0;
    let invalidCount = 0;
    
    log(`Verifying MDZ file: ${mdzPath}`, parsedArgs);
    log('--------------------', parsedArgs);
    
    for (const asset of assets) {
      log(`Checking asset: ${asset.originalName}`, parsedArgs, 'verbose');
      
      try {
        // 获取资源内容
        const content = await mdz.getAssetContent(asset.id);
        
        // 手动验证校验和，使用导入的calculateChecksum函数
        const calculatedChecksum = await calculateChecksum(content);
        const isValid = calculatedChecksum === asset.checksum;
        
        if (isValid) {
          validCount++;
          log(`  ✓ ${asset.originalName}`, parsedArgs, 'verbose');
        } else {
          invalidCount++;
          allValid = false;
          log(`  ✗ ${asset.originalName} - Checksum verification failed`, parsedArgs, 'error');
        }
      } catch (err) {
        invalidCount++;
        allValid = false;
        log(`  ✗ ${asset.originalName} - Error during verification: ${err}`, parsedArgs, 'error');
      }
    }
    
    // 显示验证结果
    log('', parsedArgs);
    log(`Verification result: ${allValid ? 'PASSED' : 'FAILED'}`, parsedArgs);
    log(`Total assets: ${assets.length}`, parsedArgs);
    log(`Valid assets: ${validCount}`, parsedArgs);
    
    if (invalidCount > 0) {
      log(`Invalid assets: ${invalidCount}`, parsedArgs, 'error');
      log('The MDZ file may be corrupted or tampered with.', parsedArgs, 'error');
    } else {
      log('All assets passed integrity verification.', parsedArgs);
    }
    
    // 关闭MDZ文件
    await mdz.close();
  } catch (err: any) {
    handleError(err, 'Failed to verify MDZ file', parsedArgs);
  }
}

// 错误处理函数
function handleError(err: any, message: string, options: any = {}) {
  if (err instanceof MDZError) {
    log(`${message}: ${err.message}`, options, 'error');
    log(`Error type: ${err.type}`, options, 'verbose');
  } else {
    log(`${message}: ${err.message || err}`, options, 'error');
  }
  
  if (options.verbose) {
    console.error(err.stack);
  }
}

// 主函数
async function main() {
  if (!command || command === 'help' || command === '--help') {
    await showHelp();
    return;
  }
  
  if (command === 'version' || command === '--version' || command === '-v') {
    showVersion();
    return;
  }
  
  const parsedArgs = parseArgs(args.slice(1));
  
  try {
    switch (command) {
      case 'create':
        await createMDZ(parsedArgs);
        break;
      case 'extract':
        await extractMDZ(parsedArgs);
        break;
      case 'info':
        await showInfo(parsedArgs);
        break;
      case 'add':
        await addAsset(parsedArgs);
        break;
      case 'list':
        await listAssets(parsedArgs);
        break;
      case 'metadata':
        await updateMetadata(parsedArgs);
        break;
      case 'password':
        await changePassword(parsedArgs);
        break;
      case 'verify':
        await verifyMDZ(parsedArgs);
        break;
      case 'extract-asset':
        await extractAsset(parsedArgs);
        break;
      default:
        log(`Error: Unknown command: ${command}`, parsedArgs, 'error');
        await showHelp();
    }
  } catch (err: any) {
    handleError(err, `An unexpected error occurred during command '${command}'`, parsedArgs);
    process.exit(1);
  }
}

// 添加错误处理
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection at:', promise, 'reason:', reason);
  process.exit(1);
});

// 执行主函数
main().catch((err) => {
  console.error('Fatal error:', err);
  process.exit(1);
}); 